// XMMATRIX alignment memory lock error solved with this thread:
// http://stackoverflow.com/questions/11285680/crash-after-m-xmmatrixidentity-aligment-memory-in-classes
// date referenced: January 23rd 2013

#ifndef OBJMESHMANAGER_H
#define OBJMESHMANAGER_H

#include "Singleton.h"
#include "ConstantBuffer.h"

#include <string>
#include <set>
#include <map>
#include <vector>
#include <Windows.h>
#include <d3d11.h>
#include <d3dx11.h>
#include <d3dcompiler.h>
#include <D3DX10math.h>
#include <stack>
#include <deque>

class VertexData;

 class ObjMesh
{
public:
	ObjMesh();
	~ObjMesh();
	bool Load(const std::string& meshName, const std::string& meshFileName, const std::wstring& shaderFile);

	D3DXMATRIX& GetMeshWorld();
	void SetWorldMatrix(const D3DXMATRIX& world);

	void Rotate(float angle, float x, float y, float z);
	void Translate(float x, float y, float z);
	void Translate(const D3DXVECTOR3& pos);
	void Scale(float x, float y, float z);
	void Scale(const D3DXVECTOR3& scale);
	void Transform(const D3DXMATRIX& mat);
	void AddTexture(const std::wstring& texturePath);
	void RemoveTexture(const std::wstring& texturePath);
	void EnableNormalMapping();
	void DisableNormalMapping();
	void SetColour(const D3DXVECTOR4& colour);

	void ChangeTexture(ID3D11ShaderResourceView* texture);
	void ChangeTexture2(ID3D11ShaderResourceView* texture);
	void ChangeShader(const std::string& shaderName);

	void Draw();
	void SetTextureTranslation(const D3DXVECTOR2& uv);

	//void* operator new(size_t i);
	//void operator delete(void* p);
	
private:
	void LoadMTL(const std::string& filename);

private:
	std::string m_meshName;
	
	std::vector<VertexData> m_vertData;
	std::vector<DWORD> m_indexData;
	std::vector<int> m_uvData;
	std::vector<int> m_normalData;
	std::vector<D3DXVECTOR2> m_uvIndices;
	std::vector<D3DXVECTOR3> m_normalIndices;
	
	ID3D11Buffer* m_vertexBuffer;
	ID3D11Buffer* m_indexBuffer;
	ID3D11Buffer* m_pConstantBuffer;

	D3D11_BUFFER_DESC m_bd;
	D3D11_SUBRESOURCE_DATA m_initData;

	D3DXMATRIX m_world;

	std::stack<D3DXMATRIX> m_transformationMatrices;

	// texture
	ID3D11ShaderResourceView* m_textureResource;
	ID3D11ShaderResourceView* m_secondTextureResource;
	ID3D11ShaderResourceView* m_normalMap;
	ID3D11SamplerState* m_textureSamplerState;
	std::set<std::wstring> m_textures;
	bool m_enableNormalMapping;

	int m_numOfIndices;

	//std::string m_useLight;

	ConstantBuffer m_cb;
	D3DXVECTOR4 m_colour;

	bool m_usingSecondBuffer;
	std::string m_shaderName;
};

class ObjMeshManager : public Uncopyable
{
	ObjMeshManager();
	friend class Singleton<ObjMeshManager>;
	friend class ObjMesh;
public:
	~ObjMeshManager();

	bool LoadObj(const std::string& fileName, const std::string& meshName, const std::wstring& shaderFile);
	void Draw(const std::string& meshName);
	
	ID3D11InputLayout*      m_pVertexLayout;
	ID3D11InputLayout* GetVertLayout() {return m_pVertexLayout;}

	D3DXMATRIX& GetMeshWorld(const std::string& meshName);
	void GetMeshWorld(const std::string& meshName, D3DXMATRIX& world);
	void SetWorldMatrix(const std::string& meshName, D3DXMATRIX& world);
	
	void Rotate(const std::string& meshName, float angle, float x, float y, float z);
	void Translate(const std::string& meshName, float x, float y, float z);
	void Translate(const std::string& meshName, const D3DXVECTOR3& pos);
	void Scale(const std::string& meshName, float x, float y, float z);
	void Scale(const std::string& meshName, const D3DXVECTOR3& scale);
	void Transform(const std::string& meshName, const D3DXMATRIX& mat);
	void AddTexture(const std::string& meshName, const std::wstring& texturePath);
	void RemoveTexture(const std::string& meshName, const std::wstring& texturePath);
	void EnableNormalMapping(const std::string& meshName);
	void DisableNormalMapping(const std::string& meshName);
	void SetColour(const std::string& meshName, const D3DXVECTOR4& colour);
	
	void ChangeTexture(const std::string& meshName, ID3D11ShaderResourceView* texture);
	void ChangeTexture2(const std::string& meshName, ID3D11ShaderResourceView* texture);
	void ChangeShader(const std::string& meshName, const std::string& shaderName);

	//void UseLight(const std::string& meshName, const std::string& lightTag);

	bool ReleaseVertBuffer();
	
private:

	//ID3D11Buffer*           m_pVertexBuffer;
	int m_vertCount;
    D3D11_BUFFER_DESC m_bd;
    D3D11_SUBRESOURCE_DATA InitData;
	std::map<std::string, ObjMesh*> m_meshes;
};

typedef Singleton<ObjMeshManager> TheObjMeshManager;

#endif OBJMESHMANAGER_H